// phalanx foreach.hpp.cu header file

// Copyright (c) 2011 - 2011 Kohei Takahashi (Flast).
// Distributed under the MIT license. for more detail see COPYING.

// Redefinition Boost.Foreach

#ifndef IG_PHALANX_FOREACH_HPP_CU_ONCE_
#define IG_PHALANX_FOREACH_HPP_CU_ONCE_

#include <phalanx/config.hpp.cu>

#include <iterator>

#include <boost/preprocessor/cat.hpp>

#include <boost/mpl/if.hpp>
#include <boost/mpl/not.hpp>
#include <boost/mpl/placeholders.hpp>

#include <phalanx/iterator/builtin_vector_iterator.hpp.cu>
#include <phalanx/iterator/reverse_iterator.hpp.cu>

#include <phalanx/utility/begin_end.hpp.cu>
#include <phalanx/utility/apply_if.hpp.cu>

#include <phalanx/type_traits/integral_constant.hpp.cu>
#include <phalanx/type_traits/builtin_traits.hpp.cu>
#include <phalanx/type_traits/is_builtin_vector.hpp.cu>
#include <phalanx/type_traits/is_array.hpp.cu>
#include <phalanx/type_traits/is_const.hpp.cu>
#include <phalanx/type_traits/is_convertible.hpp.cu>
#include <phalanx/type_traits/add_lvalue_reference.hpp.cu>
#include <phalanx/type_traits/add_const.hpp.cu>
#include <phalanx/type_traits/remove_const.hpp.cu>

#define LO_PHALANX_FOREACH_ID( _id ) \
  BOOST_PP_CAT( \
    BOOST_PP_CAT( _lo_phalanx_foreach_id_, _id ), \
    BOOST_PP_CAT( _, __LINE__ ) )

namespace phalanx
{

namespace foreach_detail
{

template < bool pred >
PHALANX_CONFIG_DEFAULT_FUNCTION( true )
integral_constant< bool, pred > *
_is_xxx( void )
{ return 0; }


template < typename T >
PHALANX_CONFIG_DEFAULT_FUNCTION( true )
false_type *
is_rvalue_( T &, int )
{ return 0; }

template < typename T, typename U >
PHALANX_CONFIG_DEFAULT_FUNCTION( true )
true_type *
is_rvalue_( T, U )
{ return 0; }


struct auto_any_base
{
    PHALANX_CONFIG_DEFAULT_FUNCTION( true )
    operator bool( void ) const
    { return false; }
};

template < typename T, typename is_rvalue >
struct auto_any
  : public auto_any_base
{
    PHALANX_CONFIG_DEFAULT_FUNCTION( true )
    auto_any( typename apply_if<
                is_rvalue,
                add_const< ::boost::mpl::_1 >,
                T >::type &x )
      : _x( x ) {}

    typename apply_if<
      ::boost::mpl::not_< is_rvalue >,
      add_lvalue_reference< ::boost::mpl::_1 >,
      T >::type _x;

    PHALANX_CONFIG_DEFAULT_FUNCTION( true )
    T &
    get( void )
    { return _x; }
};

template < typename T, ::std::size_t n, typename is_rvalue >
struct auto_any< T[ n ], is_rvalue >
  : public auto_any_base
{
    typedef T array_t[ n ];

    PHALANX_CONFIG_DEFAULT_FUNCTION( true )
    auto_any( array_t &x )
      : _x( x ) {}

    array_t &_x;

    PHALANX_CONFIG_DEFAULT_FUNCTION( true )
    array_t &
    get( void )
    { return _x; }
};

typedef const auto_any_base & auto_any_t;

template < typename T, typename is_rvalue >
PHALANX_CONFIG_DEFAULT_FUNCTION( true )
T &
auto_any_cast( auto_any_t _x )
{
    return const_cast< auto_any< T, is_rvalue > & >(
      reinterpret_cast< const auto_any< T, is_rvalue > & >(_x ) ).get();
}


template < typename Sequence,
  bool = is_array< Sequence >::value,
  bool = is_builtin_vector< typename remove_const< Sequence >::type >::value >
struct traits_adpt;

template < typename Sequence >
struct maybe_reverce_iterator
{
    PHALANX_PP_RETYPEDEF( Sequence, iterator );
    typedef ::std::iterator_traits< iterator > _traits_type;

    struct _lazy_empty_reverce_iterator
    {
        struct invalid_iterator_type {};
        typedef invalid_iterator_type reverse_iterator;
        typedef invalid_iterator_type const_reverse_iterator;
    };

    typedef typename ::boost::mpl::if_<
      is_convertible<
        typename _traits_type::iterator_category,
        ::std::bidirectional_iterator_tag >,
      Sequence,
      _lazy_empty_reverce_iterator >::type _maybe_reverce_iterator;
    PHALANX_PP_RETYPEDEF( _maybe_reverce_iterator, reverse_iterator );
    PHALANX_PP_RETYPEDEF( _maybe_reverce_iterator, const_reverse_iterator );
};

template < typename Sequence >
struct traits_adpt< Sequence, false, false >
{
    PHALANX_PP_RETYPEDEF( Sequence, reference );
    PHALANX_PP_RETYPEDEF( Sequence, iterator );
    PHALANX_PP_RETYPEDEF( maybe_reverce_iterator< Sequence >, reverse_iterator );
};

template < typename Sequence >
struct traits_adpt< const Sequence, false, false >
{
    PHALANX_PP_CONST_RETYPEDEF( Sequence, reference );
    PHALANX_PP_CONST_RETYPEDEF( Sequence, iterator );
    PHALANX_PP_CONST_RETYPEDEF( maybe_reverce_iterator< Sequence >, reverse_iterator );
};

template < typename _Vector >
struct traits_adpt< _Vector, false, true >
{
    typedef typename remove_const< _Vector >::type Vector;
    typedef vector_type_traits< Vector >           _traits_type;

    typedef typename ::boost::mpl::if_<
      is_const< _Vector >,
      typename _traits_type::const_reference,
      typename _traits_type::reference >::type reference;
    typedef builtin_vector_iterator< _Vector > iterator;
    typedef reverse_iterator< iterator >       reverse_iterator;
};

template < typename T, ::std::size_t n >
struct traits_adpt< T[ n ], true, false >
{
    typedef T &                          reference;
    typedef T *                          iterator;
    typedef reverse_iterator< iterator > reverse_iterator;
};


// value forwarding
template < typename Sequence, typename is_rvalue >
PHALANX_CONFIG_DEFAULT_FUNCTION( true )
auto_any< Sequence, is_rvalue >
forward( Sequence &seq, is_rvalue * )
{ return auto_any< Sequence, is_rvalue >( seq ); }

// begin
template < typename Sequence >
PHALANX_CONFIG_DEFAULT_FUNCTION( true )
auto_any< typename traits_adpt< Sequence >::iterator, true_type >
gbegin( Sequence &seq )
{
    using ::phalanx::begin;
    return begin( seq );
}

template < typename Sequence, typename is_rvalue >
PHALANX_CONFIG_DEFAULT_FUNCTION( true )
auto_any< typename traits_adpt< Sequence >::iterator, true_type >
begin( auto_any_t seq, Sequence *, is_rvalue * )
{ return gbegin( auto_any_cast< Sequence, is_rvalue >( seq ) ); }


// rbegin
template < typename Sequence >
PHALANX_CONFIG_DEFAULT_FUNCTION( true )
auto_any< typename traits_adpt< Sequence >::reverse_iterator, true_type >
grbegin( Sequence &seq )
{
    using ::phalanx::rbegin;
    return rbegin( seq );
}

template < typename Sequence, typename is_rvalue >
PHALANX_CONFIG_DEFAULT_FUNCTION( true )
auto_any< typename traits_adpt< Sequence >::reverse_iterator, true_type >
rbegin( auto_any_t seq, Sequence *, is_rvalue * )
{ return grbegin( auto_any_cast< Sequence, is_rvalue >( seq ) ); }


// end
template < typename Sequence >
PHALANX_CONFIG_DEFAULT_FUNCTION( true )
auto_any< typename traits_adpt< Sequence >::iterator, true_type >
gend( Sequence &seq )
{
    using ::phalanx::end;
    return end( seq );
}

template < typename Sequence, typename is_rvalue >
PHALANX_CONFIG_DEFAULT_FUNCTION( true )
auto_any< typename traits_adpt< Sequence >::iterator, true_type >
end( auto_any_t seq, Sequence *, is_rvalue * )
{ return gend( auto_any_cast< Sequence, is_rvalue >( seq ) ); }


// rend
template < typename Sequence >
PHALANX_CONFIG_DEFAULT_FUNCTION( true )
auto_any< typename traits_adpt< Sequence >::reverse_iterator, true_type >
grend( Sequence &seq )
{
    using ::phalanx::rend;
    return rend( seq );
}

template < typename Sequence, typename is_rvalue >
PHALANX_CONFIG_DEFAULT_FUNCTION( true )
auto_any< typename traits_adpt< Sequence >::reverse_iterator, true_type >
rend( auto_any_t seq, Sequence *, is_rvalue * )
{ return grend( auto_any_cast< Sequence, is_rvalue >( seq ) ); }


// increment
template < typename Sequence, typename is_reverse >
PHALANX_CONFIG_DEFAULT_FUNCTION( true )
auto_any_t
inc( auto_any_t _itr, Sequence *, is_reverse * )
{
    typedef typename ::boost::mpl::if_<
      is_reverse,
      typename traits_adpt< Sequence >::reverse_iterator,
      typename traits_adpt< Sequence >::iterator >::type iterator;
    ++auto_any_cast< iterator, true_type >( _itr );
    return _itr;
}


// compare
template < typename Sequence, typename is_reverse >
PHALANX_CONFIG_DEFAULT_FUNCTION( true )
bool
comp( auto_any_t _itr1, auto_any_t _itr2, Sequence *, is_reverse * )
{
    typedef typename ::boost::mpl::if_<
      is_reverse,
      typename traits_adpt< Sequence >::reverse_iterator,
      typename traits_adpt< Sequence >::iterator >::type iterator;
    return auto_any_cast< iterator, true_type >( _itr1 )
      == auto_any_cast< iterator, true_type >( _itr2 );
}


// dereferencing
template < typename Sequence, typename is_reverse >
PHALANX_CONFIG_DEFAULT_FUNCTION( true )
typename traits_adpt< Sequence >::reference
deref( auto_any_t _itr, Sequence *, is_reverse * )
{
    typedef typename ::boost::mpl::if_<
      is_reverse,
      typename traits_adpt< Sequence >::reverse_iterator,
      typename traits_adpt< Sequence >::iterator >::type iterator;
    return *auto_any_cast< iterator, true_type >( _itr );
}


// typeof helper
template < typename Sequence >
PHALANX_CONFIG_DEFAULT_FUNCTION( true )
Sequence *
to_ptr( Sequence &seq )
{ return &seq; }

// utility
#define LO_PHALANX_FOREACH_TYPEOF( _value ) \
  ( true ? 0 : ::phalanx::foreach_detail::to_ptr( _value ) )

#define LO_PHALANX_FOREACH_IS_RVALUE( _expression ) \
  ( true ? 0 : ::phalanx::foreach_detail::is_rvalue_( _expression, 0 ) )

#define LO_PHALANX_FOREACH_VARIABLE( _type, _sequence ) \
  ::phalanx::foreach_detail::_type LO_PHALANX_FOREACH_ID( _sequence )

#define LO_PHALANX_FOREACH_FORWARD( _sequence ) \
  ::phalanx::foreach_detail::forward( _sequence, \
    LO_PHALANX_FOREACH_IS_RVALUE( _sequence ) )


// glvalue/rvalue adapter
#define LO_PHALANX_FOREACH_ITR_ADPT( _op, _storage, _sequence ) \
  ::phalanx::foreach_detail::_op( \
    LO_PHALANX_FOREACH_ID( _storage ), \
    LO_PHALANX_FOREACH_TYPEOF( _sequence ), \
    LO_PHALANX_FOREACH_IS_RVALUE( _sequence ) )

#define LO_PHALANX_FOREACH_BEGIN( _storage, _sequence ) \
  LO_PHALANX_FOREACH_ITR_ADPT( begin, _storage, _sequence )

#define LO_PHALANX_FOREACH_END( _storage, _sequence ) \
  LO_PHALANX_FOREACH_ITR_ADPT( end, _storage, _sequence )

#define LO_PHALANX_FOREACH_RBEGIN( _storage, _sequence ) \
  LO_PHALANX_FOREACH_ITR_ADPT( rbegin, _storage, _sequence )

#define LO_PHALANX_FOREACH_REND( _storage, _sequence ) \
  LO_PHALANX_FOREACH_ITR_ADPT( rend, _storage, _sequence )


// glvalue adapter
#define LO_PHALANX_FOREACH_GITR_ADPT( _op, _sequence ) \
  ::phalanx::foreach_detail::_op( _sequence )

#define LO_PHALANX_FOREACH_GBEGIN( _sequence ) \
  LO_PHALANX_FOREACH_GITR_ADPT( gbegin, _sequence )

#define LO_PHALANX_FOREACH_GEND( _sequence ) \
  LO_PHALANX_FOREACH_GITR_ADPT( gend, _sequence )

#define LO_PHALANX_FOREACH_GRBEGIN( _sequence ) \
  LO_PHALANX_FOREACH_GITR_ADPT( grbegin, _sequence )

#define LO_PHALANX_FOREACH_GREND( _sequence ) \
  LO_PHALANX_FOREACH_GITR_ADPT( grend, _sequence )


// unary operator adapter
#define LO_PHALANX_FOREACH_UNARY( _op, _iterator, _sequence, _is_reverse ) \
  ::phalanx::foreach_detail::_op( \
    LO_PHALANX_FOREACH_ID( _iterator ), \
    LO_PHALANX_FOREACH_TYPEOF( _sequence ), \
    ::phalanx::foreach_detail::_is_xxx< _is_reverse >() )

#define LO_PHALANX_FOREACH_INC( _iterator, _sequence, _is_reverse ) \
  LO_PHALANX_FOREACH_UNARY( inc, _iterator, _sequence, _is_reverse )

#define LO_PHALANX_FOREACH_DEREF( _iterator, _sequence, _is_reverse ) \
  LO_PHALANX_FOREACH_UNARY( deref, _iterator, _sequence, _is_reverse )

#define LO_PHALANX_FOREACH_COMP( _itr1, _itr2, _sequence, _is_reverse ) \
  ::phalanx::foreach_detail::comp( LO_PHALANX_FOREACH_ID( _itr1 ), \
    LO_PHALANX_FOREACH_ID( _itr2 ), LO_PHALANX_FOREACH_TYPEOF( _sequence ), \
    ::phalanx::foreach_detail::_is_xxx< _is_reverse >() )


// foreach statement for glvalue/rvalue
#define PHALANX_FOREACH( _value, _sequence ) \
  if ( LO_PHALANX_FOREACH_VARIABLE( auto_any_t, seq ) = \
        LO_PHALANX_FOREACH_FORWARD( _sequence ) ) {} else \
  if ( LO_PHALANX_FOREACH_VARIABLE( auto_any_t, itr ) = \
        LO_PHALANX_FOREACH_BEGIN( seq, _sequence ) ) {} else \
  if ( LO_PHALANX_FOREACH_VARIABLE( auto_any_t, end ) = \
        LO_PHALANX_FOREACH_END( seq, _sequence ) ) {} else \
  for ( ; !LO_PHALANX_FOREACH_COMP( itr, end, _sequence, false ); \
        LO_PHALANX_FOREACH_INC( itr, _sequence, false ) ) \
      if ( bool LO_PHALANX_FOREACH_ID( once ) = false ) {} else \
          for ( _value = LO_PHALANX_FOREACH_DEREF( itr, _sequence, false ); \
                !LO_PHALANX_FOREACH_ID( once ); \
                LO_PHALANX_FOREACH_ID( once ) = true )

// foreach statement for glvalue
#define PHALANX_GFOREACH( _value, _sequence ) \
  if ( LO_PHALANX_FOREACH_VARIABLE( auto_any_t, itr ) = \
        LO_PHALANX_FOREACH_GBEGIN( _sequence ) ) {} else \
  if ( LO_PHALANX_FOREACH_VARIABLE( auto_any_t, end ) = \
        LO_PHALANX_FOREACH_GEND( _sequence ) ) {} else \
  for ( ; !LO_PHALANX_FOREACH_COMP( itr, end, _sequence, false ); \
        LO_PHALANX_FOREACH_INC( itr, _sequence, false ) ) \
      if ( bool LO_PHALANX_FOREACH_ID( once ) = false ) {} else \
          for ( _value = LO_PHALANX_FOREACH_DEREF( itr, _sequence, false ); \
                !LO_PHALANX_FOREACH_ID( once ); \
                LO_PHALANX_FOREACH_ID( once ) = true )

// reverse-foreach statement for glvalue/rvalue
#define PHALANX_REVERSE_FOREACH( _value, _sequence ) \
  if ( LO_PHALANX_FOREACH_VARIABLE( auto_any_t, seq ) = \
        LO_PHALANX_FOREACH_FORWARD( _sequence ) ) {} else \
  if ( LO_PHALANX_FOREACH_VARIABLE( auto_any_t, itr ) = \
        LO_PHALANX_FOREACH_RBEGIN( seq, _sequence ) ) {} else \
  if ( LO_PHALANX_FOREACH_VARIABLE( auto_any_t, end ) = \
        LO_PHALANX_FOREACH_REND( seq, _sequence ) ) {} else \
  for ( ; !LO_PHALANX_FOREACH_COMP( itr, end, _sequence, true ); \
        LO_PHALANX_FOREACH_INC( itr, _sequence, true ) ) \
      if ( bool LO_PHALANX_FOREACH_ID( once ) = false ) {} else \
          for ( _value = LO_PHALANX_FOREACH_DEREF( itr, _sequence, true ); \
                !LO_PHALANX_FOREACH_ID( once ); \
                LO_PHALANX_FOREACH_ID( once ) = true )

// reverse-foreach statement for glvalue
#define PHALANX_REVERSE_GFOREACH( _value, _sequence ) \
  if ( LO_PHALANX_FOREACH_VARIABLE( auto_any_t, itr ) = \
        LO_PHALANX_FOREACH_GRBEGIN( _sequence ) ) {} else \
  if ( LO_PHALANX_FOREACH_VARIABLE( auto_any_t, end ) = \
        LO_PHALANX_FOREACH_GREND( _sequence ) ) {} else \
  for ( ; !LO_PHALANX_FOREACH_COMP( itr, end, _sequence, true ); \
        LO_PHALANX_FOREACH_INC( itr, _sequence, true ) ) \
      if ( bool LO_PHALANX_FOREACH_ID( once ) = false ) {} else \
          for ( _value = LO_PHALANX_FOREACH_DEREF( itr, _sequence, true ); \
                !LO_PHALANX_FOREACH_ID( once ); \
                LO_PHALANX_FOREACH_ID( once ) = true )

} // namespace foreach_detail

} // namespace phalanx

#endif // IG_PHALANX_FOREACH_HPP_CU_ONCE_

